package edu.ucla.wanda.db;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Vector;

import javax.faces.model.SelectItem;

public class WandaSubjectDbAdapter {
	private WandaSubject subject;
	private String id;
	private String measurementToken;
	private Vector<WandaSensorMeasurement> measurements;
	private HashMap<String, WandaSensorSpec> sensorSpecs;
	private String readingTimeSort, readingValueSort;
	//private String sensorIdFilter; TODO
	
	private static final String SORT_ASCENDING = "sort_ascending";
	private static final String SORT_DESCENDING = "sort_descending";
	private static final String SORT_NOSORT = "sort_nosort";
	
	private static final String DB_ADDRESS = "http://wanda.elasticbeanstalk.com";
	
	public WandaSubjectDbAdapter(String id) throws IOException{
		URL url = null;
		
		try {
			url = new URL(DB_ADDRESS + "/subjects/" + id);
		} catch (MalformedURLException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		HttpURLConnection cxn = (HttpURLConnection) url.openConnection();
		
		cxn.setRequestMethod("GET");
		cxn.setRequestProperty("Accept", "application/json");
		
		InputStream jsonStream = cxn.getInputStream();
		JsonElement element = new JsonParser().parse(new InputStreamReader(jsonStream));
		
		cxn.disconnect();
		this.id = id;
		this.subject = new WandaSubject(element);
		this.measurements = new Vector<WandaSensorMeasurement>(50, 25);
		this.sensorSpecs = new HashMap<String, WandaSensorSpec>();
		
		try {
			ReadSensorMeasurements(true, 50);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public WandaSubject getSubject()
	{
		return subject;
	}
	
	public Vector<WandaSensorMeasurement> getMeasurements() {
		return measurements;
	}
	
	/**
	 * @return the sensorSpecs
	 */
	public HashMap<String, WandaSensorSpec> getSensorSpecs() {
		return sensorSpecs;
	}

	/**
	 * @return the timeSort
	 */
	public String getReadingTimeSort() {
		return readingTimeSort;
	}

	/**
	 * @param timeSort the timeSort to set
	 */
	public void setReadingTimeSort(String timeSort) {
		this.readingTimeSort = timeSort;
		this.readingValueSort = SORT_NOSORT;
	}

	/**
	 * @return the readingValueSort
	 */
	public String getReadingValueSort() {
		return readingValueSort;
	}

	/**
	 * @param readingValueSort the readingValueSort to set
	 */
	public void setReadingValueSort(String readingValueSort) {
		this.readingValueSort = readingValueSort;
		this.readingTimeSort = SORT_NOSORT;
	}
	
	public void sortByReadingTime()
	{
		if(SORT_NOSORT.equals(getReadingTimeSort()) || SORT_DESCENDING.equals(getReadingTimeSort()))
		{
			setReadingTimeSort(SORT_ASCENDING);
			Collections.sort(measurements, WandaSensorMeasurement.getSensorTimeComparatorAsc());
		}
		
		else
		{
			setReadingTimeSort(SORT_DESCENDING);
			Collections.sort(measurements, WandaSensorMeasurement.getSensorTimeComparatorDesc());
		}
	}
	
	
	
	public void sortByReadingValue()
	{
		if(SORT_NOSORT.equals(getReadingValueSort()) || SORT_DESCENDING.equals(getReadingValueSort()))
		{
			setReadingValueSort(SORT_ASCENDING);
			Collections.sort(measurements, WandaSensorMeasurement.getSensorValueComparatorAsc());
		}
		
		else
		{
			setReadingValueSort(SORT_DESCENDING);
			Collections.sort(measurements, WandaSensorMeasurement.getSensorValueComparatorDesc());
		}
	}

	public int ReadSensorMeasurements(boolean bReplace, int minRead) throws IOException
	{
		int nRead = 0;
		JsonParser parser = new JsonParser();
		
		if(bReplace)
		{
			measurementToken = null;
			measurements.clear();
		}
		
		setReadingTimeSort(SORT_DESCENDING);

		URL measurementsUrl, sensorUrl;
		
		do
		{
			try {
				measurementsUrl = new URL(DB_ADDRESS + "/subjects/" + id + "/measurements?order=StartTime%20desc");
			} catch (MalformedURLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				return nRead;
			}
			
			HttpURLConnection cxn = (HttpURLConnection) measurementsUrl.openConnection();
			//cxn.addRequestProperty("order", "StartTime%20desc");
			
			if(null != measurementToken)
			{
				cxn.addRequestProperty("next", measurementToken);
			}
			
			cxn.setRequestMethod("GET");
			cxn.setRequestProperty("Accept", "application/json");
			
			InputStream jsonStream = cxn.getInputStream();
			
			JsonObject root = parser.parse(new InputStreamReader(jsonStream)).getAsJsonObject();
			
			cxn.disconnect();

			// get token
			JsonElement tokenElement = root.get("next");
			if(!tokenElement.isJsonNull())
			{
				measurementToken = tokenElement.getAsString();
			}
			
			else
			{
				measurementToken = null;
			}
			
			JsonArray measurementArray = root.getAsJsonArray("measurements");
			
			// step through all the measurements
			for(int i = 0; i < measurementArray.size(); i++)
			{
				// create a measurement object
				WandaSensorMeasurement m = new WandaSensorMeasurement(measurementArray.get(i));
				
				// sensor spec not available?  retrieve and store.
				if(!sensorSpecs.containsKey(m.getSensorId()))
				{
					sensorUrl = new URL(DB_ADDRESS + "/sensors/" + m.getSensorId());
					HttpURLConnection sensorCxn = (HttpURLConnection) sensorUrl.openConnection();
					
					sensorCxn.setRequestMethod("GET");
					sensorCxn.setRequestProperty("Accept", "application/json");
					
					InputStream sensorStream = sensorCxn.getInputStream();
					JsonObject sensorObj = parser.parse(new InputStreamReader(sensorStream)).getAsJsonObject();
					sensorCxn.disconnect();
					
					WandaSensorSpec spec = new WandaSensorSpec(sensorObj);
					sensorSpecs.put(m.getSensorId(), spec);
				}
				
				m.setSensorSpec(sensorSpecs.get(m.getSensorId()));
				
				measurements.add(m);
			}
		
					
			// no more sensor readings, or read more than minimum?
			if(null == measurementToken || nRead > minRead)
			{
				return nRead;
			}
		}
		while(nRead < minRead);
		
		return nRead;		
	}
	
	public Vector<SelectItem> getSensorIds()
	{
		Vector<SelectItem> sensorIds = new Vector<SelectItem>(sensorSpecs.values().size());
		
		sensorIds.add(new SelectItem("", "(all)"));
		
		for(WandaSensorSpec spec : sensorSpecs.values())
		{
			sensorIds.add(new SelectItem(spec.getId(), spec.getId()));
		}
		
		return sensorIds;
	}
	
	/*
	public void setSensorIdFilter(String sensor)
	{
		sensorIdFilter = sensor;
	}
	
	/*
	public Filter<?> getFilterSensorId() {
        return new Filter<WandaSensorMeasurement>() {
            public boolean accept(WandaSensorMeasurement t) {
                String sensorIds = getSensorIdFilter();
                if (sensorIds == null || sensorIds.length() == 0 || "(all)".equals(sensorIds) || sensorIds.contains(t.getSensorSpec().getId())) {
                    return true;
                }
                return false;
            }
        };
    }*/
	/*
	public String getSensorIdFilter()
	{
		if(null == sensorIdFilter)
		{
			sensorIdFilter = "(all)";
		}
		
		return sensorIdFilter;
	}*/

	public boolean Authenticate(String password) {
		MessageDigest md;
		try {
			md = MessageDigest.getInstance("MD5");
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
		String calculatedPassword = id + subject.getName() + subject.getGender();
		
		md.update(calculatedPassword.getBytes());
		byte byteData[] = md.digest();
		
        //convert the byte to hex format method 1
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < byteData.length; i++) {
         sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1));
        }
 
		//return (password == sb.toString().substring(0, 10));
        // TODO - fix authentication
        return true;
	}
}
